package cn.edu.neu.lab603.cloudeval.monitor

import java.util.concurrent.atomic.AtomicInteger

import breeze.linalg.DenseMatrix
import breeze.numerics._
import cn.edu.neu.lab603.cloudeval.CloudEval
import cn.edu.neu.lab603.cloudeval.entity.DataCenter
import cn.edu.neu.lab603.des.SimMonitor
import cn.edu.neu.lab603.math.DCRank

import scala.collection.{Map, mutable}

/** 网络流量监控监视器。
  *
  * @param span 时间间隔
  * @author yfwz100
  */
class NetworkUtilityMonitor(val span: Int = 10) extends SimMonitor[CloudEval] {

  protected val netMap = new mutable.HashMap[(Long, Long), mutable.ListMap[Int, Int]]

  override def isMatch(context: CloudEval): Boolean =
    context.getCurrentTask.isInstanceOf[DataCenter.NetworkTransferEvent]

  override def before(context: CloudEval): Unit = {}

  override def after(context: CloudEval): Unit = {
    val clock = context.getCurrentClock
    val task = context.getCurrentTask.asInstanceOf[DataCenter.NetworkTransferEvent]

    // 根据数据包确定当前的网络流去向
    val packet = task.packet
    val q = netMap.getOrElseUpdate((packet.from, packet.to), new mutable.ListMap[Int, Int])
    q(clock) = packet.length
    for ((k, v) <- q) {
      if (clock - 10 > k) {
        q -= k
      }
    }
  }

  def getVMRank(context: CloudEval): Map[Int, Double] = {
    // 重新生成 "ID - 名称" 组合
    val nameMap = new mutable.HashMap[Int, Int]()
    val idGen = new AtomicInteger()

    // 获取 VM 规模
    val vmScale = context.dataCenters.map(_.pmList.map(host => {
      host.getVMList.map(vm => nameMap.getOrElseUpdate(vm.id.toInt, idGen.getAndIncrement())).size
    }).sum).sum

    if (vmScale <= 0) return Map.empty

    // 生成虚拟机的邻接表矩阵
    val mat = netMap
      // 由于网络表是被动更新的，因此，需要根据当前时间进行修正
      .mapValues(c => c.filterKeys(_ > context.getCurrentClock - 10).values.sum)
      .filter(_._2 > 0)
      // 根据当前 span 时间内的网络流构建网络流量表
      .foldLeft(DenseMatrix.zeros[Double](vmScale, vmScale)) { (m, p) =>
      val ((n1, n2), _) = p
      m.update(nameMap(n1.toInt) -> nameMap(n2.toInt), 1)
      m.update(nameMap(n2.toInt) -> nameMap(n1.toInt), 1)
      m
    }

    // 根据当前网络状况，计算 VM 的重要性排名
    val rank = DCRank(mat)

    nameMap.mapValues(rank(_))
  }

  /** 获取当前数据中心总的平衡性。
    *
    * @param context 仿真环境
    * @return 当前的均衡水平
    */
  def getBalanceLevel(context: CloudEval): Double = {
    // 获取 VM 规模
    val vmRank = getVMRank(context)
    val vmScale = vmRank.size

    context.dataCenters.map { dc =>
      val hosts = dc.pmList.map(_.getVMList.map(vm => vmRank(vm.id.toInt)).sum)
      hosts.map(_ / hosts.sum).map(p => if (p > 0) log(p * vmScale) * p else 0).sum
    }.sum
  }

}
